#include <iostream>
#include "process_mgmt.h"
#include "stream.h"

using namespace std;


ostream &stream::os   = cout;
ostream &stream::oerr = cerr;
istream &stream::is   = cin;
UINT_PTR const stream::ostreamMutex          = processes::mutex_create(UNNAMED);
UINT_PTR const stream::ostreamSystemMutex    = processes::mutex_create(UNNAMED);
UINT_PTR const stream::ostreamDesynchroMutex = processes::mutex_create(UNNAMED);
BOOL stream::desynchroPending  = FALSE;

static string create_output_string(const string &module, const string &output);

FunctionSuccesFailure stream::error_message(const string &module, const string &output)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

#if _ERROR_NOTIFICATION_
    static const string error_signature = "[(!!) error] ";
    fresult = error_output_message(error_signature + module, output);
#endif

    return fresult;
}

FunctionSuccesFailure stream::system_message(const string &module, const string &output)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

#if _SYSTEM_NOTIFICATION_
    static const string system_signature = "[system] ";
    fresult = output_message(system_signature + module, output); 
#endif

    return fresult;
}

FunctionSuccesFailure stream::output_message(const string &module, const string &output)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

    if( desynchroPending == FALSE && processes::mutex_take(ostreamMutex, ostreamWaitMsTimeout) == FUNCTION_SUCCESS ) {
        os<<create_output_string(module, output);

        processes::mutex_release(ostreamMutex);
        fresult = FUNCTION_SUCCESS;
    }
    else if(processes::mutex_take(ostreamSystemMutex, ostreamWaitMsTimeout) == FUNCTION_SUCCESS) {
        os<<create_output_string(module, output);

        processes::mutex_release(ostreamSystemMutex);
        fresult = FUNCTION_SUCCESS;
    }
    else {
        //no action defined
    }

    return fresult;
}

FunctionSuccesFailure stream::error_output_message(const string &module, const string &err_output)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

    if( desynchroPending == FALSE && processes::mutex_take(ostreamMutex, ostreamWaitMsTimeout) == FUNCTION_SUCCESS ) {
        oerr<<create_output_string(module, err_output);

        processes::mutex_release(ostreamMutex);
        fresult = FUNCTION_SUCCESS;
    }
    else if(processes::mutex_take(ostreamSystemMutex, ostreamWaitMsTimeout) == FUNCTION_SUCCESS) {
        oerr<<create_output_string(module, err_output);

        processes::mutex_release(ostreamSystemMutex);
        fresult = FUNCTION_SUCCESS;
    }
    else {
        //no action defined
    }

    return fresult;
}

ostream &stream::get_os()
{ 
    static UINT_PTR   mutex_current_owner  = NULL;

    if(processes::mutex_take(ostreamMutex, ostreamWaitMsTimeout) == FUNCTION_FAILURE)
        desynchronized_threads_handler(ostreamMutex, mutex_current_owner);
    else if(desynchroPending == TRUE) 
        desynchronization_source_handler(mutex_current_owner);

    mutex_current_owner = processes::thread_get_current();
    os.flush();

    return os;
}

VOID stream::print() 
{
    processes::mutex_release(ostreamMutex);
}

VOID stream::desynchronized_threads_handler(UINT_PTR mutex_blocked, UINT_PTR mutex_current_owner)
{
    static UINT_PTR delayQueueEvent = processes::event_create(TRUE, UNNAMED);

    if(processes::mutex_take(ostreamDesynchroMutex, 0) == FUNCTION_FAILURE) {
        processes::mutex_take_infinite(delayQueueEvent);
        processes::mutex_take_infinite(mutex_blocked);
    }
    else {
        processes::event_reset(delayQueueEvent);
        if(processes::mutex_take(mutex_blocked, ostreamWaitMsTimeout) == FUNCTION_FAILURE) {
            desynchroPending = TRUE;
            error_message("stream", "ostream desynchronization occured! Possibility of PRINTOUT() being neglected in thread: "
                                    +decToHexString(mutex_current_owner)+"\nall threads using ostream are scheduled to be SUSPENDED!");

            processes::event_signal(delayQueueEvent);
            processes::mutex_take_infinite(mutex_blocked);

            error_message("stream", "ostream synchronization RESTORED!");
            desynchroPending = FALSE;  
        }

        processes::event_signal(delayQueueEvent);
        processes::mutex_release(ostreamDesynchroMutex);
    }

}

VOID stream::desynchronization_source_handler(UINT_PTR mutex_current_owner)
{
    error_message("stream", "ostream invoked by corrupted thread: ["+decToHexString(mutex_current_owner)+"] again!\n"+
                            "functionality in deadlock state - emergency program shutdown!"); 
    exit(EXIT_FAILURE);
}

string create_output_string(const string &module, const string &output)
{
    static const  CHAR new_line    = '\n';
    static const  string separator = ": ";
    string ostring = new_line+module+separator;

    for(UINT i=0, k=output.size(); i<k; ++i) {
        if(output[i] == new_line)
            ostring += new_line+module+separator;
        else
            ostring += output[i];
    }
    ostring += new_line;

    return ostring;
}

